#include <vector>
#include <algorithm>
#include <iostream>

using namespace std;

#include "typedefs.h"
#include "Evaluator.h"
#include "QueryTree2.h"
#include "QueryTreeTuple.h"
#include "VariableTable.h"


Evaluator::Evaluator(Result* r, Symbol* s, Relation* rel, WithClause* w, VarTable* v/*, AST* ast, ... etc*/) {
	symbolTable = s;
	resultList = r;
	relationList = rel;
	withList = w;
	varTable = v;
}

void Evaluator::evaluate() {
	//assuming all entities can be represented by an int/index eg. procedure index
	
	vector<vector<unsigned int>> possibilities;

	for(int i = 0;i<symbolTable->getSize();i++) {
		vector<unsigned int> temp;
		possibilities.push_back(temp);
		unsigned int j;
		int size;
		design_entity type = symbolTable->getSymbolType(i);

		switch(type) {
			/*
		case procedure:
			//possibilities[i] =  "get from procedure table all the procedure index stored in vector<int>"
			size = procTable->getSize();
			
			for(j = 0; j<size; j++){
				possibilities[i].push_back(j);
			}
			break;
		case stmt:
			//possibilities[i] = "get from ast all the possible statement index stored in vector<int>"
			possibilities[i].push_back(ast->getAllIfInd());
			possibilities[i].push_back(ast->getAllWhileInd());
			possibilities[i].push_back(ast->getAllAssignInd());
			possibilities[i].push_back(ast->getAllCallInd());
			break;
		case assign:
			//possibilities[i] = "get from ast all the possible assignment statement index"
			possibilities[i].push_back(ast->getAllAssignInd());
			break;
		case call:
			//possibilities[i] = "get from ast or call table all the possible call statement index"
			possibilities[i].push_back(ast->getAllCallInd());
			break;
		case while_:
			//possibilities[i] = "get from ast all the possible while statement index
			possibilities[i].push_back(ast->getAllWhileInd());
			break;
		case if_:
			//possibilities[i] = "get from ast all the possible if statement index
			possibilities[i].push_back(ast->getAllIfInd());
			break;
			*/
		case variable:
			//possibilities[i] = "get from variable table all the possible variable index"
			size = varTable->getSize();
			
			for(j = 0; j<size; j++){
				possibilities[i].push_back(j);
			}

			break;
		default:
			//stmtList or invalidDesign_Entity
			break;
		
		}

	}
	
	for(int i = 0;i <withList->getSize();i++) {
		//assume correct match of synonym type to attribute reference, done by processor
		//there won't be any case where procedure p; select ... with p.varName = "x"
		vector<unsigned int> result;
		vector<unsigned int> v;
		vector<unsigned int>::iterator it;

		WithClauseTuple temp = withList->getWithClauseTupleAt(i);
		symbolIndex index = temp.getSymbol();
		attr_Name attribute = temp.getAttrName();
		refType referenceType = temp.getRefType();
		/*
		if(referenceType == number) {
			int ref = temp.getNumRef();
		
			switch(attribute){
			case value:						//case of c.value = 5 or any number
				
				//call a function from constant table
				//vector<int> result = "get from constant table index of constant with value = ref"

				result = constantTable->getConstant(ref);

				//possibilities[index] = "intersection of result and possibilities[index]" 
				//must do intersection rather than just assigning possibilities[index] = result;
				v.resize(result.size()+possibilities[index].size());
			
				sort (result.begin(), result.end());
				sort (possibilities[index].begin(), possibilities[index].end());

				it=set_intersection(result.begin(), result.end(), possibilities[index].begin(), possibilities[index].end(), v.begin());


				break;
			case stmtNum:				//case of s.stmt# = 5 or any number, s can be type stmt, if, while, assign
				
				//vector<int> result = "get from ast the index of stmt with stmt number = ref"
				
				result.push_back(ast->getNode(ref));
				//possibilities[index] = "intersection of result and possibilities[index]"

				v.resize(result.size()+possibilities[index].size());
			
				sort (result.begin(), result.end());
				sort (possibilities[index].begin(), possibilities[index].end());

				it=set_intersection(result.begin(), result.end(), possibilities[index].begin(), possibilities[index].end(), v.begin());

				break;
			default:
				break;
			}
		}*/
		if(referenceType == name) {
			string ref = temp.getNameRef();
			vector<unsigned int>::iterator it2;
			vector<unsigned int>temp2;
			switch(attribute) {
			/*case procName:						//case of p.procName = "something"
				
				//vector<int> result = "get from procedure table index of procedure with procName = ref"
				result.push_back(proctable->getProcIndex(ref));

				//possibilities[index] = "intersection of result and possibilities[index]" 
				v.resize(result.size()+possibilities[index].size());
			
				sort (result.begin(), result.end());
				sort (possibilities[index].begin(), possibilities[index].end());

				it=set_intersection(result.begin(), result.end(), possibilities[index].begin(), possibilities[index].end(), v.begin());

				break;*/
			case varName:						//case of v.varName = "something"
				
				//vector<int> result = "get from variable table the index of variable with varName = ref"
				result.push_back(varTable->getVarI(ref));
				//cout<<ref<<endl;
				//for(int i = 0;i< result.size();i++) {
					//cout<<result[i]<<endl;
				//}
				//possibilities[index] = "intersection of result and possibilities[index]"
				v.resize(result.size()+possibilities[index].size());
			
				sort (result.begin(), result.end());
				sort (possibilities[index].begin(), possibilities[index].end());

				it=set_intersection(result.begin(), result.end(), possibilities[index].begin(), possibilities[index].end(), v.begin());
				
				
				
				
				for(it2=v.begin();it2<=it;it2++) {
					temp2.push_back(v[*it2]);
				}

				possibilities[index] = temp2;

				break;
			default:
				break;
			}
		}
	}

	/*for(int i = 0;i < relationList->getSize();i++) {
		RelationTuple temp = relationList->getRelationTupleAt(i);
		relationType type = temp.getrelRef();
		refType arg1Type = temp.getRefType(1);
		refType arg2Type = temp.getRefType(2);
		string nameArg1;
		string nameArg2;
		symbolIndex symbolArg1;
		symbolIndex symbolArg2;
		int numArg1;
		int numArg2;
		
		vector<int> result;
		vector<int> v;
		vector<int>::iterator it;

		switch(arg1Type) {
		case name:
			nameArg1 = temp.getNameArg(1);
			break;
		case number:
			numArg1 = temp.getNumberArg(1);
			break;
		case symbol:
			symbolArg1 = temp.getSymbolArg(1);
			break;
		default:
			break;
		}

		switch(arg2Type) {
		case name:
			nameArg2 = temp.getNameArg(2);
			break;
		case number:
			numArg2 = temp.getNumberArg(2);
			break;
		case symbol:
			symbolArg2 = temp.getSymbolArg(2);
			break;
		default:
			break;
		}

		switch(type) {
		case modifies:
			// call function from pkb according to the argument type
			if(!(nameArg1.empty()) && !(nameArg2.empty())){
				result.push_back(procTable->isModifiesProc(nameArg1, nameArg2));
			}
			else if(!(nameArg1.empty()) && numArg2 != NULL){
				break;
			}
			else if(!(nameArg1.empty()) && symbolArg2 != NULL){
				int procIndex = procTable->getProcIndex(nameArg1);
				result.push_back(procTable.getModifies(procIndex));
			}
			else if(numArg1 != NULL && !(nameArg2.empty())){
				result.push_back(modifiesTable->isModifiesStmt(numArg1, nameArg2));
			}
			else if(numArg1 != NULL &&  numArg2 != NULL){
				break;
			}
			else if(numArg1 != NULL && symbolArg2 != NULL){
				result.push_back(modifiesTable->getModifies(numArg1));
			}
			else if(symbolArg1 != NULL && !(nameArg2.empty())){
				design_entity type = symbolTable->getSymbolTupleAt(symbolArg1).getType();

				switch(type){
				case procedure:

				case stmt:

				default:
				}
			}
			else if(symbolArg1 != NULL && numArg2 != NULL){
				break;
			}
			else if(symbolArg1 != NULL && symbolArg2 != NULL){

			}
			// intersect with possibilities
			break;
		case uses:
			break;
		case calls:
			break;
		case callsT:
			break;
		case parent:
			break;
		case parentT:
			break;
		case follows:
			break;
		case followsT:
			break;
		default:
			break;
		}
	}*/
	if(resultList->isBooleanResult()) {

	}
	else {
		vector<unsigned int> answer;	
		for(int i = 0;i<resultList->getSize();i++) {
			ResultClauseTuple r = resultList->getResultClauseTupleAt(i);
			answer = possibilities[r.getSymbolIndex()];
		}

		for(int i = 0;i<answer.size();i++) {
			cout<<answer[i]<<endl;
		}
	}
}